package com.share.tools;

import com.alibaba.fastjson.JSONObject;
import com.share.tools.time.DateUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import ma.glasnost.orika.*;
import ma.glasnost.orika.converter.BidirectionalConverter;
import ma.glasnost.orika.converter.ConverterFactory;
import ma.glasnost.orika.converter.builtin.DateToStringConverter;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.Type;
import net.sf.cglib.beans.BeanCopier;
import net.sf.cglib.core.Converter;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang3.StringUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author caifan
 * @date 2019/10/16
 */
@Slf4j
public class BeanConvertUtils {
    private static MapperFactory mapperFactory;
    private static MapperFacade mapperFacade;

    static {
        mapperFactory = new DefaultMapperFactory.Builder().build();
        /*mapperFactory.classMap(User1.class, User2.class)
                .field("username", "username")
                .customize(new UserCustomMapper())
                .register();*/
        mapperFacade = mapperFactory.getMapperFacade();
    }

    private static <S, D> BoundMapperFacade<S, D> getBoundMapperFacade(Class<S> srcClazz, Class<D> dstClazz) {
        return mapperFactory.getMapperFacade(srcClazz, dstClazz);
    }

    /**
     * 将类型为 S 的 src 对象转换成类型为 D 的对象
     *
     * @param src
     * @param dstClazz
     * @param <S>
     * @param <D>
     * @return
     */
    public static <S, D> D map(S src, Class<D> dstClazz) {
        if (src == null) {
            return null;
        }
        if (src instanceof User1) {
            mapperFactory.classMap(User1.class, User2.class)
                    .field("username", "username")
                    .customize(new UserCustomMapper())
                    .register();
            mapperFacade = mapperFactory.getMapperFacade();
        }
        return mapperFacade.map(src, dstClazz);
//        return getBoundMapperFacade((Class<S>) src.getClass(), dstClazz).map(src);
    }

    public <S, D> void copy(S source, D dest, Map<String, String> fieldMap) throws Exception {
        BeanInfo sourceInfo = Introspector.getBeanInfo(source.getClass());
//        BeanInfo destInfo = Introspector.getBeanInfo(dest.getClass());
        PropertyDescriptor[] sourcePropertyDescriptors = sourceInfo.getPropertyDescriptors();
//        PropertyDescriptor[] destPropertyDescriptors = destInfo.getPropertyDescriptors();

        for (PropertyDescriptor propertyDescriptor : sourcePropertyDescriptors) {
            Method readMethod = propertyDescriptor.getReadMethod();
            if ("birth".equals(propertyDescriptor.getName())) {

            } else {

            }
        }
    }

    /**
     * 将类型为 S 的 src 对象集合，转换成类型为 D 的对象列表
     *
     * @param srcCollection
     * @param srcClazz
     * @param dstClazz
     * @param <S>
     * @param <D>
     * @return
     */
    public static <S, D> List<D> listMap(Collection<S> srcCollection, Class<S> srcClazz, Class<D> dstClazz) {
        if (srcCollection == null) {
            return null;
        }

        BoundMapperFacade<S, D> boundMapperFacade = getBoundMapperFacade(srcClazz, dstClazz);

        List<D> dstList = new ArrayList<>(srcCollection.size());
        for (S s : srcCollection) {
            D d = boundMapperFacade.map(s);
            dstList.add(d);
        }
        return dstList;
    }

    /**
     * 将类型为 S 的 src 对象列表，转换成类型为 D 的对象列表
     *
     * @param srcCollection
     * @param dstClazz
     * @param <S>
     * @param <D>
     * @return
     */
    public static <S, D> List<D> listMap(List<S> srcCollection, Class<D> dstClazz) {
        if (srcCollection == null) {
            return null;
        }

        if (srcCollection.size() == 0) {
            return Collections.emptyList();
        }

        BoundMapperFacade<S, D> boundMapperFacade = getBoundMapperFacade((Class<S>) srcCollection.get(0).getClass(), dstClazz);

        List<D> dstList = new ArrayList<>(srcCollection.size());
        for (S s : srcCollection) {
            D d = boundMapperFacade.map(s);
            dstList.add(d);
        }
        return dstList;
    }

    /**
     * 将类型为 S 的 src 对象集合，转换成类型为 <K, D> 的Map, 主键的值使用每个对象的 keyFieldName 域的值，例如 refId
     *
     * @param srcCollection
     * @param srcClazz
     * @param dstClazz
     * @param keyFieldName
     * @param <S>
     * @param <D>
     * @param <K>
     * @return
     */
    public static <S, D, K> Map<K, D> mapByFieldMap(Collection<S> srcCollection, Class<S> srcClazz, Class<D> dstClazz, String keyFieldName) throws NoSuchFieldException, IllegalAccessException {
        BoundMapperFacade<S, D> boundMapperFacade = getBoundMapperFacade(srcClazz, dstClazz);
        Field keyField = dstClazz.getDeclaredField(keyFieldName);
        keyField.setAccessible(true);

        Map<K, D> dstMap = new HashMap(srcCollection.size());
        for (S s : srcCollection) {
            D d = boundMapperFacade.map(s);
            K keyValue = (K) keyField.get(d);
            dstMap.put(keyValue, d);
        }
        return dstMap;
    }

    /**
     * 忽略 id created updated deleted version
     *
     * @param src
     * @param dstClazz
     * @param <S>
     * @param <D>
     * @return
     */
    public static <S, D> D mapIgnoreGenericKey(S src, Class<D> dstClazz) {
        if (src == null) {
            return null;
        }
        mapperFactory.classMap(src.getClass(), dstClazz)
                .exclude("id")
                .exclude("created")
                .exclude("updated")
                .exclude("deleted")
                .exclude("version")
                .byDefault()
                .register();
        return mapperFactory.getMapperFacade().map(src, dstClazz);
    }

    /**
     * 对象转换，类型不同的属性忽略
     * @param src
     * @param dClass
     * @throws Exception
     */
    public static <S, D> D convertBeanIgnoreType(S src, Class<D> dClass) {
        D dest;
        try {
            dest = dClass.newInstance();
        } catch (Exception e) {
            log.error("class instance error:{}", e.getMessage());
            return null;
        }
        copyAllFieldValue(src, dest, dClass, true);
        return dest;
    }

    /**
     * 对象转换，类型不同的属性忽略
     * @param src 原对象
     * @param dest 被转换对象
     * @param cover true 原对象属性在目标对象有且类型一样则完全覆盖 false:只覆盖值不为空的
     * @throws Exception
     */
    public static <S, D> D mergeBeanIgnoreType(S src, D dest, Boolean cover) {
        Class dClass = dest.getClass();
        copyAllFieldValue(src, dest, dClass, cover);
        return dest;
    }

    /**
     * 将原有对象的数据复制到目标对象根据cover 确定是否用旧值覆盖新值(包含null值)
     * @param src
     * @param dest
     * @param dClass
     * @param cover
     * @param <S>
     * @param <D>
     * @return
     */
    private static <S, D> D copyAllFieldValue(S src, D dest, Class<D> dClass, Boolean cover) {
        List<Field> allFields = new ArrayList<>();
        while (dClass != null && !dClass.getName().equals("java.lang.Object")) {
            allFields.addAll(Arrays.asList(dClass.getDeclaredFields()));
            dClass = (Class<D>) dClass.getSuperclass();
        }

        for (Field field : allFields) {
            PropertyDescriptor srcProp = null;
            PropertyDescriptor destProp = null;
            try {
                srcProp = new PropertyDescriptor(field.getName(), src.getClass());
                destProp = new PropertyDescriptor(field.getName(), dest.getClass());
                if (Objects.isNull(srcProp)) {
                    continue;
                }
                /**
                 * 仅对类型相同属性进行转换 cover true完全覆盖 false只覆盖非空值
                 */
                if (srcProp.getPropertyType().equals(destProp.getPropertyType())) {
                    Method getMethod = srcProp.getReadMethod();
                    Object transValue = getMethod.invoke(src);
                    Method destMethod = destProp.getReadMethod();
                    Object destValue = destMethod.invoke(dest);
                    //完全覆盖
                    if (cover) {
                        Method setMethod = destProp.getWriteMethod();
                        setMethod.invoke(dest, transValue);
                    } else {
                        /**
                         * 目标对象原本有值的不做修改
                         */
                        if (!Objects.isNull(destValue)) {
                            Method setMethod = destProp.getWriteMethod();
                            setMethod.invoke(dest, destValue);
                        } else {
                            Method setMethod = destProp.getWriteMethod();
                            setMethod.invoke(dest, transValue);
                        }
                    }
                }
            } catch (Exception e) {
                log.error("beanconvert error:{}", e.getMessage());
                continue;
            }
        }
        return dest;
    }

    /**
     *  将json中的相同名称 相同类型的字段值复制到具体对象，
     * @param t
     * @param json
     * @param cover true 用新值覆盖旧值 false 不覆盖
     * @param <T>
     * @return
     */
    public static <T> T mergeJSON(T t, JSONObject json, Boolean cover) {
        try {
            BeanInfo beanInfo = Introspector.getBeanInfo(t.getClass());
            PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
            for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
                Class<?> propertyType = propertyDescriptor.getPropertyType();
                String propertyName = propertyDescriptor.getName();
                if (".class".equals(propertyName)) {
                    continue;
                }
                Object value = json.get(propertyName);
                /*
                 * json值不为空，类型相同则覆盖
                 */
                if (!Objects.isNull(value)) {
                    if (propertyType.getTypeName().equals(value.getClass().getTypeName()) && cover) {
                        Method writeMethod = propertyDescriptor.getWriteMethod();
                        writeMethod.invoke(t, value);
                    }
                }
            }
        } catch (Exception e) {
            log.error("bean merge json error:{}", e.getMessage());
        }
        return t;
    }

    public static void main(String[] args) {
        //ConverterFactory converterFactory = mapperFactory.getConverterFactory();
        //converterFactory.registerConverter("dateRegister", new DateToStringConverter("yyyy-MM-dd HH:mm:ss"));
        User1 user1 = new User1();
//        User2 user2 = new User2();
        user1.setBirth("1991-01-01 12:12:32");
        user1.setUsername("zhangsan");
        user1.setGender("2");
        /*mapperFactory.classMap(user1.getClass(), user2.getClass())
                .field("username", "username")
                .fieldMap("birth", "birth")
                .converter("dateRegister").add().register();
        mapperFacade = mapperFactory.getMapperFacade();
        mapperFacade.map(user1, user2);
        System.out.println(user2.toString());*/


//        mapperFacade = mapperFactory.getMapperFacade();
//        mapperFacade.map(user1, user2);
        User2 user2 = map(user1, User2.class);

//        System.out.println(user2.toString());
//
//        copy(user1, user2);
//        User2 user21 = convertBean(user1, User2.class);

//        System.out.println(user21.toString());
    }

    private static <S, D> void copy(S source, D dest) {
//        BeanCopier copier = BeanCopier.create(user1.getClass(), user2.getClass(), true);
        BeanCopier copier = BeanCopier.create(source.getClass(), dest.getClass(), true);
        copier.copy(source, dest, new MyConverter());
    }

}

class MyConverter implements Converter {

    @Override
    public Object convert(Object value, Class target, Object context) {
        String s = value.toString();
        if (target.equals(Date.class)) {
            return DateUtils.str2Date(s, DateUtils.PARTTERN_YMDHMS);
        }
        return value;
    }
}

class UserCustomMapper extends CustomMapper<User1, User2> {
    @Override
    public void mapAtoB(User1 user1, User2 user2, MappingContext context) {
        if (StringUtils.isNotEmpty(user1.getBirth())) {
            Date date = DateUtils.str2Date(user1.getBirth(), DateUtils.PARTTERN_YMDHMS);
            user2.setBirth(date);
        }
    }

    @Override
    public void mapBtoA(User2 user2, User1 user1, MappingContext context) {
        if (null != user2.getBirth()) {
            String date = DateUtils.date2String(user2.getBirth(), DateUtils.PARTTERN_YMDHMS);
            user1.setBirth(date);
        }
    }
}

/**
 * 单项转换器
 */
class DateConverter extends CustomConverter<String, Date> {

    @Override
    public Date convert(String source, Type<? extends Date> destinationType, MappingContext mappingContext) {
        return null;
    }
}

class MyDateConverter extends BidirectionalConverter<String,Date> {

    @Override
    public Date convertTo(String source, Type<Date> destinationType, MappingContext mappingContext) {
        return null;
    }

    @Override
    public String convertFrom(Date source, Type<String> destinationType, MappingContext mappingContext) {
        return null;
    }
}

@Data
class User1 {
    private String birth;
    private String username;
    private String gender;
    private Integer status;
    private List<String> url;
}

@Data
class User2 {
    private Date birth;
    private String username;
    private Integer gender;
    private String status;
    private String url;
}